home *** CD-ROM | disk | FTP | other *** search
/ TeX 1995 July / TeX CD-ROM July 1995 (Disc 1)(Walnut Creek)(1995).ISO / dviware / ivd2dvi / ivd2dvi.c < prev    next >
C/C++ Source or Header  |  1990-10-01  |  33KB  |  958 lines

  1. /*
  2.  *  ivd2dvi.
  3.  *  Copyright 1988 by Larry Denenberg.  May be freely distributed as long
  4.  *  as this notice is retained.
  5.  *  
  6.  *  This is a general discussion of ivd2dvi, assuming familiarity with
  7.  *  the format of DVI files and with the semantics of the reflection
  8.  *  commands (see Knuth and Mackay, "Mixing right-to-left text with
  9.  *  left-to-right text" in TUGboat volume 8 number 1).  Notation: let
  10.  *  "BR" stand for the BEGIN_REFLECT command, let "ER" stand for
  11.  *  END_REFLECT, let "a" stand for SET_097 (i.e.  "typeset character 97,"
  12.  *  usually lowercase a), and similarly for the other lowercase letters.
  13.  *  
  14.  *  The simplest idea would be to rearrange reflected text somehow so
  15.  *  that the right thing happens.  Is it enough to reverse the commands?
  16.  *  Certainly if we see "BR a b c ER" we can replace it with "c b a".
  17.  *  But this works only so long as the reflected segment sticks to a
  18.  *  small subset of DVI commands; "BR a FONT_00 b ER" can't in general be
  19.  *  changed to "b FONT_00 a".  We also get in trouble with commands that
  20.  *  change the DVI registers (not to mention PUSH and POP!).
  21.  *  
  22.  *  Some of these difficulties could be overcome with more clever
  23.  *  rearrangement.  Unfortunately, there are situations that can't be
  24.  *  handled by any such scheme:  for example, you can't typeset the
  25.  *  sequence "BR PUSH i POP w ER" without knowing about the widths of
  26.  *  "i" and "w".  The PUTn commands also lead to unsolvable problems.
  27.  *  So we abandon this approach and resign ourselves to reading width
  28.  *  data from TFM files.
  29.  *  
  30.  *  Here's the method we use:  we read through the input DVI file,
  31.  *  copying DVI commands to the output file.  When we see a BR we stop
  32.  *  copying and simply read the input looking for the matching ER.
  33.  *  During this scan we keep track of the total width of the commands
  34.  *  between the BR and ER.  (Each character contributes its width as
  35.  *  revealed by the TFM file, horizontal motions contribute their
  36.  *  width, vertical motions are ignored, etc.)  This procedure is
  37.  *  called *simulating*; at its end we've just read an ER and we know
  38.  *  the width of the segment between the BR and the ER.  Call this
  39.  *  width T.  Note that nothing at all is output during simulation.
  40.  *  
  41.  *  Now we move right (in the output file) by T; of course we do so by
  42.  *  shipping out a RIGHTn command.  Next, we make a second pass over the
  43.  *  commands between the BR and the ER; this time, we typeset them
  44.  *  "backwards."  For example, to SET an "a" we first move left by the
  45.  *  width of an "a", and lay down the "a" without moving---this is the
  46.  *  inverse of laying down the "a" and then moving right.  If the command
  47.  *  is any sort of horizontal motion we negate the distance before moving.
  48.  *  Reflection applies only to horizontal motion, however; vertical
  49.  *  motion commands, font changes, font definitions, and so forth, are
  50.  *  copied unchanged.  (For the precise way in which we reverse, see the
  51.  *  documentation for the individual DVI commands below, especially
  52.  *  /SetChar/ and /SetString/.)
  53.  *
  54.  *  When we see the ER for the second time, the horizontal location in
  55.  *  the output file should be the same as it was when we first saw the BR
  56.  *  (since we've processed all the same commands, only backwards).  So we
  57.  *  once again output a command to move right by T, thus moving to the
  58.  *  place where we typeset the first command after the BR.  We're now
  59.  *  done translating this reflected segment and we continue normally.
  60.  *  
  61.  *  What happens when reflections are nested?  Well, it's easy.  If we
  62.  *  encounter a BR during a simulation, we basically ignore it!  We're
  63.  *  trying to find the total width of the reflected segment; the fact
  64.  *  that part of the segment is further reflected doesn't matter.  (We do
  65.  *  have to be certain to skip over the inner ER so that we match only
  66.  *  the ER we're looking for.)  Later, when we're no longer simulating
  67.  *  but "typesetting backwards," we'll encounter the BR of the inner
  68.  *  reflection.  At that point it gets its own simulation set up, and we
  69.  *  do the whole two-pass business again on the inner segment; of course,
  70.  *  the second pass is now a "forwards" pass, at the end of which we
  71.  *  continue the "backwards" pass over the outer segment.
  72.  *  
  73.  *  All of this can be nested to any depth.  We keep track of what we're
  74.  *  doing with the variable /State/ which takes on the following values:
  75.  *      LTYPESETTING  Typesetting normally, left-to-right
  76.  *      RTYPESETTING  Typesetting "backwards," right-to-left
  77.  *      SIMULATING    First pass over text to be reflected
  78.  *     >SIMULATING    Inside nested begin_reflect/end_reflect pairs
  79.  *  Values of /State/ greater than SIMULATING are used to keep track of
  80.  *  the nesting depth of BR/ER pairs.  While we're simulating, we simply
  81.  *  increment /State/ when we see BR and decrement it when we see ER.
  82.  *  We switch from simulating to typesetting only when we see ER while
  83.  *  /State/ is exactly equal to SIMULATING.  Note carefully that we're
  84.  *  never simulating on behalf of more than one BR/ER pair; there's never
  85.  *  more than one segment whose width we're measuring.
  86.  *  
  87.  *  So the procedure upon hitting a BR in the general case is as follows:
  88.  *  if /State/ equals or exceeds SIMULATING, just increment /State/.
  89.  *  Otherwise set /State/ to SIMULATING and start the first pass.  The
  90.  *  actions necessary at ER are a bit more complex: if /State/ is
  91.  *  *greater* than SIMULATING, just decrement it.  If /State/ is equal to
  92.  *  SIMULATING, we've just finished a simulation: output the motion
  93.  *  command as above, switch to the direction opposite to the one we were
  94.  *  in when we hit the BR, and reset the input file to the point just
  95.  *  after the BR, thus beginning the second pass.  If /State/ is
  96.  *  LTYPESETTING or RTYPESETTING, we've just finished the second pass
  97.  *  over a reflection:  invert /State/, and output the second long motion
  98.  *  command as described above.  Of course, there's lots of things to
  99.  *  save and restore here; details are in the code below.
  100.  *  
  101.  *  Even when we're not simulating we have to keep track of certain
  102.  *  values.  The current font is an obvious example: we have to remember
  103.  *  what it is, because if we start a simulation we'll have to measure
  104.  *  its characters.  We also keep track of the horizontal motion
  105.  *  parameters: if a W0 appears in reflected text we must know the
  106.  *  current value of W, which may have been set before the simulation
  107.  *  began.  Therefore we keep variables in which we store the values, and
  108.  *  a stack to model all pushes and pops in the input file.  The same
  109.  *  stack is used to store values over a simulation; no conflict can
  110.  *  occur.  On the other hand, the values of the vertical motion
  111.  *  parameters never concern us.
  112.  *  
  113.  *  One final twist.  Suppose we encounter the following sequence: "BR
  114.  *  <set W to d> a W0 b W0 c W0 ER" Now if we weren't clever, each W0
  115.  *  would have to be replaced by a longer RIGHTn command, because when
  116.  *  we're typesetting backwards a W0 translates to a rightward motion by
  117.  *  -d, not d.  It's better to set W to -d to begin with; then we can use
  118.  *  W0 inside the reflection and save considerable space.  So we negate
  119.  *  the parameter of Wn commands encountered while RTYPESETTING.  But as
  120.  *  a consequence, the input file may disagree with the output file over
  121.  *  the current value of W.  So we must keep two separate variables,
  122.  *  /WInput/ and /WOutput/, that record the two values.  These two
  123.  *  variables always are equal in absolute value, but we can't compute
  124.  *  from the state whether they're equal or not (because reflections may
  125.  *  start and end independently of changes to W).  More details are given
  126.  *  at /SetWandMoveForward/.  All of this applies to X as well.  With
  127.  *  more work we could do further optimization, catching (e.g.)  cases
  128.  *  like "<set W to d> BR a W0 b W0 ER" in which W0 will not be used as
  129.  *  our scheme stands.
  130.  *  
  131.  *  Jacques Goldberg first suggested the possibility of a dvi-ivd to dvi
  132.  *  processor.  Please send comments, suggestions, and bug reports to
  133.  *  larry@bbn.com or larry@harvard.edu.
  134.  *  
  135.  */
  136.  
  137.  
  138. #include <stdio.h>
  139. #include "global.h"
  140. #include "commands.h"
  141.  
  142. /*  Global variables and procedures defined in auxiliary.c.  */
  143. extern font *CurFont, *FindFont();
  144. extern void Initializations(), BadDVIAbort(), MaxPushLevelOutput();
  145. extern void PushWrite(), PopWrite();
  146. extern void PushWX(), PopXW(), PushDeltaH(), PopDeltaH();
  147. extern void FontDefine(), CopyFontDefinition();
  148.  
  149. /*  Global variables and procedures defined in io.c.  */
  150. extern unsigned BufSize, SignedBytes();
  151. extern long BytesOutput, CopyWord();
  152. extern long ReadSigned(), ReadUnsigned(), CharWidth(), CopyUnsigned();
  153. extern void WriteByte(), WriteString(), WriteNumber(), WriteWord();
  154. extern void CopyNBytes(), SkipNBytes();
  155. extern void RereadLastByte(), ResetFilePosition();
  156. extern unsigned_byte CopyByte(), ReadByte(), ReadCommand();
  157. extern unsigned_byte *ReadFilePosition();
  158.  
  159.  
  160. /* Global variables defined here */
  161. char   *ProgramName;        /* argv[0], used in error messages    */
  162. boolean    VerboseOutput = FALSE;    /* v flag: report page progress        */
  163. boolean    ExactOutput = FALSE;    /* X flag: try not to change input file    */
  164. long    PrevPagePointer = -1;    /* output file location of previous BOP    */
  165. int    State;            /* current direction or simulation depth*/
  166. long    WInput, WOutput;    /* value of W in input and output files    */
  167. long    XInput, XOutput;    /* value of X in input and output files    */
  168. long    DeltaH;            /* total size of reflected segment    */
  169. int    SavedState;        /* state at start of current simulation    */
  170. font   *SavedFont;        /* font at start of current simulation    */
  171. unsigned_byte *SavedPosition;    /* file position at start of simulation    */
  172. unsigned_byte  CurCommand;    /* DVI command under consideration    */
  173.  
  174. #define REVERSE(STATE)    (LTYPESETTING + RTYPESETTING - STATE)
  175.  
  176.  
  177. /* Procedures defined in this file, in order of definition */
  178. void main(),Arguments(),FileArgument(),Preliminaries(),MainLoop();
  179. void SetChar(),SetRule(),SetString(),SetFont();
  180. void BeginPage(),EndPage(),BegReflect(),EndReflect();
  181. void MoveForward(),SetWandMoveForward(),SetXandMoveForward();
  182. void Postliminaries(),CopyParametrizedCommand();
  183.  
  184.  
  185.  
  186.  
  187. /*
  188.  *  Main procedure, whose function is self-documenting.  The only way
  189.  *  ivd2dvi can terminate normally is through the /exit/ here.
  190.  */
  191. void
  192. main(ignore, argv)
  193. int ignore;
  194. char *argv[];
  195. {
  196.   Arguments(argv);
  197.   Initializations();
  198.   Preliminaries();
  199.   MainLoop();
  200.   Postliminaries();
  201.   exit(0);
  202. }
  203.  
  204.  
  205.  
  206. /*
  207.  *  Standard argument processing.  Don't worry if a flag is given more
  208.  *  than once, but allow at most one filename.  We also allow forms like
  209.  *  "-Xv", or even "-Xbv 1024" since the argument following any -b is
  210.  *  taken as the new buffer size.
  211.  */
  212. void
  213. Arguments(argv)
  214. char *argv[];
  215. {
  216.   char *arg;
  217.   boolean seenfile = FALSE;
  218.   int newbufsize;
  219.  
  220.   ProgramName = argv[0];
  221.   for (arg = *++argv; arg; arg = *++argv)
  222.     if (*arg == '-')
  223.       while (*++arg)
  224.     switch(*arg) {
  225.       case 'X':
  226.         ExactOutput = TRUE;
  227.         break;
  228.       case 'v':
  229.         VerboseOutput = TRUE;
  230.         break;
  231.       case 'b':
  232.         if (!*++argv) {
  233.               fprintf(stderr, "%s: missing buffer size, -b ignored\n",
  234.                   ProgramName);
  235.           return;
  236.         } else {
  237.           newbufsize = atoi(*argv);
  238.           if (newbufsize == 0)
  239.         fprintf(stderr, "%s: illegal buffer size %s ignored\n",
  240.                 ProgramName, *argv);
  241.           else
  242.         BufSize = newbufsize;
  243.         }
  244.         break;
  245.       default:
  246.         fprintf(stderr, "%s: illegal flag %c ignored\n",
  247.                 ProgramName, *arg);
  248.     }
  249.     else if (seenfile)
  250.       fprintf(stderr, "%s: superflous filename %s ignored\n",
  251.               ProgramName, arg);
  252.     else {
  253.       seenfile = TRUE;
  254.       FileArgument(arg);
  255.     }
  256. }
  257.  
  258.  
  259.  
  260. /*
  261.  *  Process a file argument.  Try to open the file.  If we can't, and if
  262.  *  the name has no period after its rightmost slash, append ".dvi" and
  263.  *  try again.  In either case we're reopening standard input, so that
  264.  *  we can read the input DVI file from /stdin/ whether or not there's a
  265.  *  filename on the command line.
  266.  */
  267. void
  268. FileArgument(filename)
  269. char *filename;
  270. {
  271.   char buf[MAXFILENAMESIZE], *p;
  272.  
  273.   if (freopen(filename, "r", stdin) != NULL) return;
  274.   p = rindex(filename, '/');
  275.   if (*p == '\0') p = filename;
  276.   p = index(p, '.');
  277.   if (*p != '\0') {
  278.     fprintf(stderr, "%s: Can't open %s\n", ProgramName, filename);
  279.     exit(1);
  280.   } else {
  281.     (void) sprintf(buf, "%s.dvi", filename);
  282.     if (freopen(buf, "r", stdin) != NULL) return;
  283.     fprintf(stderr, "%s: Can't open %s nor %s\n",
  284.             ProgramName, filename, buf);
  285.     exit(1);
  286.   }
  287. }
  288.  
  289.  
  290.  
  291. /*
  292.  *  Process the preamble.  We mostly just copy it through untouched,
  293.  *  except that we check the first two bytes for correctness and update
  294.  *  the comment string to say ivd2dvi was here.  (Don't update the
  295.  *  comment string if the -X flag was used nor when the new comment
  296.  *  wouldn't fit.)
  297.  */
  298. void
  299. Preliminaries()
  300. {
  301.   static char *comment = "; postprocessed by ivd2dvi";
  302.   unsigned comlength;
  303.  
  304.   if (CopyByte() != PRE) BadDVIAbort("no preamble");
  305.   if (CopyByte() != DVIVERSION)
  306.     BadDVIAbort("wrong DVI version in preamble");
  307.   CopyNBytes(12L);
  308.   comlength = ReadByte();
  309.   if (!ExactOutput && (comlength < 256 - strlen(comment))) {
  310.     WriteByte(comlength + strlen(comment));
  311.     CopyNBytes((long) comlength);
  312.     WriteString(comment);
  313.   } else {
  314.     WriteByte(comlength);
  315.     CopyNBytes((long) comlength);
  316.   }
  317. }
  318.  
  319.  
  320.  
  321.  
  322. /*
  323.  *  The main loop.  Read a command, switch on it, and continue doing so
  324.  *  forever.  The only way out of this routine is when /CurCommand/ is
  325.  *  POST.  Most cases of the main switch are simply procedure calls; the
  326.  *  rest are documented case-by-case.  We first handle the most common
  327.  *  case, for top speed:  if /CurCommand/ is SET_000 through SET_127 and
  328.  *  we're typesetting normally, we need do nothing but write it into the
  329.  *  output.  If we're simulating or typesetting backwards, /SetString/
  330.  *  will handle it.  Other commands are handled inside the switch.  Note:
  331.  *  the first test relies on the assumption that SET_000 is 0, which we
  332.  *  really shouldn't do, but then again it *is* the test for the most
  333.  *  common case!   Expressions like "CurCommand - W1 + 1" return the
  334.  *  number of bytes in the first parameter of commands with four forms.
  335.  */
  336.  
  337. #define SETTING TRUE
  338.  
  339. void
  340. MainLoop()
  341. {
  342.   while (TRUE) {
  343.  
  344.     CurCommand = ReadCommand();
  345.  
  346.     if (CurCommand <= SETC_127) {
  347.       if (State == LTYPESETTING) WriteByte(CurCommand); else SetString();
  348.       continue;
  349.     }
  350.  
  351.     switch (CurCommand) {
  352.  
  353.       case SET1: case SET2: case SET3: case SET4:
  354.     SetChar(CurCommand - SET1 + 1,  SETTING);
  355.     break;
  356.  
  357.       case PUT1: case PUT2: case PUT3: case PUT4:
  358.     SetChar(CurCommand - PUT1 + 1, !SETTING);
  359.     break;
  360.  
  361.       case SET_RULE: case PUT_RULE:
  362.     SetRule();
  363.     break;
  364.  
  365.       case NOP:
  366.     if (ExactOutput) WriteByte(NOP);
  367.     break;
  368.  
  369.       case BOP:
  370.     BeginPage();
  371.     break;
  372.  
  373.       case EOP:
  374.     EndPage();
  375.     break;
  376.  
  377.  
  378. /*
  379.  *  When we see a PUSH/POP we save/restore the horizontal parameters.
  380.  *  If we're simulating, we also have to save/restore /DeltaH/, since
  381.  *  stuff that happens between the PUSH and the POP has no effect on
  382.  *  the size of the reflected segment.  (If we're not simulating, we
  383.  *  don't care at all about /DeltaH/ and there's no sense saving it.)
  384.  *  Note that the stack discipline works out as long as we restore
  385.  *  in the opposite order that we save, since PUSH/POP must nest with
  386.  *  respect to reflection---so we can't have a PUSH while simulating
  387.  *  that matches a POP while not simulating.  Oh yeah, I almost forgot:
  388.  *  if we're *not* simulating, we must write the command to the output!
  389.  */
  390.       case PUSH:
  391.     PushWX();
  392.     if (State < SIMULATING) PushWrite(); else PushDeltaH();
  393.     break;
  394.  
  395.       case POP:
  396.     if (State < SIMULATING) PopWrite(); else PopDeltaH();
  397.     PopXW();
  398.     break;
  399.  
  400.       case RIGHT1: case RIGHT2: case RIGHT3: case RIGHT4:
  401.     MoveForward(ReadSigned(CurCommand - RIGHT1 + 1));
  402.     break;
  403.  
  404.  
  405. /*
  406.  *  Move forward by W.  It's up to /MoveForward/ whether "forward" means
  407.  *  left or right (or neither, if we're just simulating).  The amount to
  408.  *  move is of course the *input* file's idea of the current W.
  409.  */
  410.       case W0:
  411.     MoveForward(WInput);
  412.     break;
  413.  
  414.       case W1: case W2: case W3: case W4:
  415.     SetWandMoveForward(ReadSigned(CurCommand - W1 + 1));
  416.     break;
  417.  
  418.  
  419. /*
  420.  *  Move forward by X.  Please see the comments for W0, above.
  421.  */
  422.       case X0:
  423.     MoveForward(XInput);
  424.     break;
  425.  
  426.       case X1: case X2: case X3: case X4:
  427.     SetXandMoveForward(ReadSigned(CurCommand - X1 + 1));
  428.     break;
  429.  
  430.  
  431. /*
  432.  *  We don't especially care about vertical motion, so if we're only
  433.  *  simulating there's nothing to do.  Otherwise, copy the command and
  434.  *  its parameters to the output file.
  435.  */
  436.       case DOWN1: case DOWN2: case DOWN3: case DOWN4:
  437.     if (State < SIMULATING)
  438.       CopyParametrizedCommand(CurCommand - DOWN1 + 1);
  439.     break;
  440.  
  441.       case Y0: case Y1: case Y2: case Y3: case Y4:
  442.     if (State < SIMULATING) CopyParametrizedCommand(CurCommand - Y0);
  443.     break;
  444.  
  445.       case Z0: case Z1: case Z2: case Z3: case Z4:
  446.     if (State < SIMULATING) CopyParametrizedCommand(CurCommand - Z0);
  447.     break;
  448.  
  449.  
  450. /*
  451.  *  We must keep track of all font changes, simulating or not.  So call
  452.  *  /SetFont/ on the selected font number, and if we're not simulating
  453.  *  copy the whole command to the output file.
  454.  */
  455.       case FNT1: case FNT2: case FNT3: case FNT4:
  456.         if (State >= SIMULATING)
  457.       SetFont(ReadUnsigned(CurCommand - FNT1 + 1));
  458.     else {
  459.       WriteByte(CurCommand);
  460.       SetFont(CopyUnsigned(CurCommand - FNT1 + 1));
  461.     }
  462.     break;
  463.  
  464.  
  465. /*
  466.  *  Skip past if simulating, otherwise copy out the whole command.
  467.  */
  468.       case XXX1: case XXX2: case XXX3: case XXX4:
  469.     if (State >= SIMULATING)
  470.       SkipNBytes(ReadUnsigned(CurCommand - XXX1 + 1));
  471.     else {
  472.       WriteByte(CurCommand);
  473.       CopyNBytes(CopyUnsigned(CurCommand - XXX1 + 1));
  474.     }
  475.     break;
  476.  
  477.       case FNT_DEF1: case FNT_DEF2: case FNT_DEF3: case FNT_DEF4:
  478.     FontDefine(CurCommand - FNT_DEF1 + 1);
  479.     break;
  480.  
  481.       case PRE:
  482.     BadDVIAbort("unexpected PRE");
  483.     break;
  484.  
  485.  
  486. /*
  487.  *  Shouldn't see the postamble while simulating or reversing; otherwise
  488.  *  it's time to quit.  This is the only normal way out of /MainLoop/.
  489.  */
  490.       case POST:
  491.     if (State != LTYPESETTING) BadDVIAbort("unexpected POST");
  492.     return;
  493.  
  494.       case POST_POST:
  495.     BadDVIAbort("unexpected POST_POST");
  496.     break;
  497.  
  498.       case BEG_REFLECT:
  499.     BegReflect();
  500.     break;
  501.  
  502.       case END_REFLECT:
  503.     EndReflect();
  504.     break;
  505.  
  506.  
  507. /*
  508.  *  The only commands left, besides illegal ones, are the one-byte font
  509.  *  selection commands.  Copy them to the output file unless simulating.
  510.  *  Then call /SetFont/ to note the change of font.
  511.  */
  512.       default:
  513.     if (CurCommand >= FONT_00 && CurCommand <= FONT_63) {
  514.       if (State < SIMULATING) WriteByte(CurCommand);
  515.       SetFont((long) CurCommand - FONT_00);
  516.     } else
  517.       BadDVIAbort("unrecognized command");
  518.  
  519.       }        /* end of "switch (CurCommand) { ... }"    */
  520.    }       /* end of "while (TRUE) { ... }"    */
  521. }    /* end of procedure MainLoop */
  522.  
  523.  
  524.  
  525.  
  526. /*
  527.  *  Set a single character as specified by a SETn or PUTn command.
  528.  *  (SET_nnn commands don't come here; they go to /SetString/.)
  529.  *  /setting/ tells whether it was a SET or a PUT, and /bytes/ gives the
  530.  *  parameter length.  If we're typesetting normally we just copy out the
  531.  *  command and parameter, and if we're simulating it's enough to add the
  532.  *  width of the desired character to the running total.  If we're
  533.  *  typesetting in reverse there's more to do: first move "forward"
  534.  *  (left, in this case) by the width of the character.  Only then do we
  535.  *  typeset the character.  Moreover, if the original command was a SET,
  536.  *  we PUT the character since we've already moved.  And if the original
  537.  *  command was a PUT, we SET the character---which has the effect of
  538.  *  moving back to the right, for a net motion of zero.
  539.  */
  540. void
  541. SetChar(bytes, setting)
  542. unsigned bytes;
  543. boolean setting;
  544. {
  545.   long charnumber;
  546.  
  547.   switch (State) {
  548.     case LTYPESETTING:
  549.       CopyParametrizedCommand(bytes);
  550.       break;
  551.     case RTYPESETTING:
  552.       charnumber = ReadUnsigned(bytes);
  553.       MoveForward(CharWidth(charnumber));
  554.       if (setting)
  555.     WriteByte(PUT1 + bytes - 1);
  556.       else
  557.     WriteByte(SET1 + bytes - 1);
  558.       WriteNumber(charnumber, bytes);
  559.       break;
  560.     default:    /* State >= SIMULATING */
  561.       DeltaH += CharWidth(ReadUnsigned(bytes));
  562.   }
  563. }
  564.  
  565.  
  566.  
  567. /*
  568.  *  Same idea as /SetChar/, just above.  If LTYPESETTING just copy, if
  569.  *  simulating just accumulate the width.  If RTYPESETTING first move
  570.  *  left and then interchange SET and PUT.
  571.  */
  572. void
  573. SetRule()
  574. {
  575.   long height, width;
  576.  
  577.   switch (State) {
  578.     case LTYPESETTING:
  579.       CopyParametrizedCommand(8);
  580.       break;
  581.     case RTYPESETTING:
  582.       height = ReadSigned(4);
  583.       width = ReadSigned(4);
  584.       MoveForward(width);
  585.       if (CurCommand == SET_RULE) WriteByte(PUT_RULE);
  586.     else WriteByte(SET_RULE);
  587.       WriteWord(height);
  588.       WriteWord(width);
  589.       break;
  590.     default:    /* State >= SIMULATING */
  591.       SkipNBytes(4L);
  592.       width = ReadSigned(4);
  593.       if (CurCommand == SET_RULE) DeltaH += width;
  594.   }
  595. }
  596.  
  597.  
  598.  
  599. /*
  600.  *  Handle a SET_nnn command.  This routine is called only while
  601.  *  simulating or typesetting in reverse (the normal case is handled
  602.  *  directly in /MainLoop/ for speed).
  603.  *
  604.  *  If we're just simulating there's nothing to do except add the width
  605.  *  of the character to the running total.  The RTYPESETTING case is the
  606.  *  interesting one.  Here's the story:  We could typeset backwards
  607.  *  as we do in /SetChar/ above:  move left, then PUT (since all these
  608.  *  commands are variants of SET).  But that would mean that every one
  609.  *  of these common one-byte commands would translate into around five
  610.  *  bytes.  So instead of handling a single command, this routine handles
  611.  *  /CurCommand/ and all subsequent SET_nnn commands at once.  We first
  612.  *  continue to read the input until we hit a command that is *not* a
  613.  *  SET_nnn.  As we do so, we accumulate the total width (call it T) of
  614.  *  all the SET_nnn commands that we've read.  Finally, we write the
  615.  *  following instructions into the output file:  (a) move forward---that
  616.  *  is, left---by T,  (b) PUSH, to save the new value of H,  (c) typeset
  617.  *  all the characters we've seen *backwards*,  (d) POP to get back to
  618.  *  the place where we started emitting characters.  This does the trick.
  619.  *
  620.  *  A few notes:  We must back up the input after we're done since that
  621.  *  first non-SET_nnn command must be reconsidered by /MainLoop/.  We
  622.  *  rely on the fact that /ReadFilePosition/ gives us a pointer into a
  623.  *  buffer from which we can rattle off /count/ characters;  this is true
  624.  *  since we must be rescanning input after a simulation.  Finally, if
  625.  *  there's only a single SET_nnn character to typeset we save a byte by
  626.  *  replacing steps (b), (c) and (d) with a simple PUT1.
  627.  *
  628.  *  This routine relies on the fact that SET_nnn in fact has value nnn,
  629.  *  which, stylistically, is absolutely indefensible.
  630.  */
  631. void
  632. SetString()
  633. {
  634.   unsigned_byte *p;
  635.   unsigned_byte nextcommand;
  636.   long totalwidth;
  637.   int count;
  638.  
  639.   if (State >= SIMULATING)
  640.     DeltaH += CharWidth((long) CurCommand);
  641.   else {        /* State == RTYPESETTING */
  642.     totalwidth = count = 0;
  643.     nextcommand = CurCommand;
  644.     do {
  645.       count++;
  646.       totalwidth += CharWidth((long) nextcommand);
  647.       nextcommand = ReadByte();
  648.     } while (nextcommand <= SETC_127);
  649.     RereadLastByte();
  650.     MoveForward(totalwidth);
  651.     if (count == 1) {
  652.       WriteByte(PUT1);
  653.       WriteByte(CurCommand);
  654.     } else {
  655.       PushWrite();
  656.       p = ReadFilePosition();
  657.       while (count--) WriteByte(*--p);
  658.       PopWrite();
  659.     }
  660.   }
  661. }
  662.  
  663.  
  664.  
  665. /*
  666.  *  Record the current font number, which must be done no matter what
  667.  *  state we're in.  The work is done by /FindFont/;  we just put the
  668.  *  result into /CurFont/ and complain if the font is unknown.
  669.  */
  670. void
  671. SetFont(fontnumber)
  672. long fontnumber;
  673. {
  674.   CurFont = FindFont(fontnumber);
  675.   if (CurFont == NULL) BadDVIAbort("font used but not defined");
  676. }
  677.  
  678.  
  679.  
  680. /*
  681.  *  Pages should end only in normal left-to-right mode.  We write the
  682.  *  BOP, copy the ten \count registers printing the first if in verbose
  683.  *  mode, and write out the pointer to the previous page.  (We can't copy
  684.  *  the old one since page lengths may change.)  We also update this
  685.  *  pointer; it must point to the newly written BOP.  Finally, we start
  686.  *  out the font, W, and X registers correctly.
  687.  */
  688. void
  689. BeginPage()
  690. {
  691.   long pagenumber;
  692.  
  693.   if (State != LTYPESETTING) BadDVIAbort("unexpected BOP");
  694.   WriteByte(BOP);
  695.   pagenumber = CopyWord();
  696.   if (VerboseOutput) fprintf(stderr, "[%ld", pagenumber);
  697.   CopyNBytes(36L);
  698.   SkipNBytes(4L);
  699.   WriteWord(PrevPagePointer);
  700.   PrevPagePointer = BytesOutput - 45;
  701.   WInput = WOutput = XInput = XOutput = 0;
  702.   CurFont = NULL;
  703. }
  704.  
  705.  
  706.  
  707. /*
  708.  *  End a page.  Must happen in "normal" LTYPESETTING mode, but otherwise
  709.  *  there's nothing to do but write the EOP and chatter if requested.
  710.  */
  711. void
  712. EndPage()
  713. {
  714.   static int pages = 0;
  715.  
  716.   if (State != LTYPESETTING) BadDVIAbort("unexpected EOP");
  717.   WriteByte(EOP);
  718.   if (VerboseOutput) fprintf(stderr, "]%c", (++pages % 10) ? ' ' : '\n');
  719. }
  720.  
  721.  
  722.  
  723. /*
  724.  *  Start of a reflected segment, which may happen in any state.  If
  725.  *  we're already simulating just increment /State/ to indicate nested
  726.  *  reflections (see the overview above).  Otherwise start simulating:
  727.  *  save the horizontal motion parameters, the current file location, the
  728.  *  current font, and the current state.  Start the running total of the
  729.  *  size of this segment at zero.  Change state to SIMULATING to start
  730.  *  the first pass over the segment; the rest of the work is done at
  731.  *  /EndReflect/.  One subtlety: it's OK to save the font, the state, and
  732.  *  the file position in variables since there's never more than one
  733.  *  simulation at a time.  We could do the same with the horizontal
  734.  *  motion parameters, but we might as well use the extant stack.
  735.  */
  736. void
  737. BegReflect()
  738. {
  739.   if (State >= SIMULATING) ++State;
  740.   else {
  741.     PushWX();
  742.     SavedPosition = ReadFilePosition();
  743.     SavedFont = CurFont;
  744.     SavedState = State;
  745.     State = SIMULATING;
  746.     DeltaH = 0;
  747.   }
  748. }
  749.  
  750.  
  751.  
  752.  
  753. /*
  754.  *  Come here at the end of a reflected segment.  We first comment the
  755.  *  easy case, which happens to be coded last:  If /State/ is greater
  756.  *  than SIMULATING we're inside nested reflections that don't concern
  757.  *  us yet, so just decrement /State/ to show that we're up a level.
  758.  *
  759.  *  If /State/ says we're at the outermost simulation, we must wrap up
  760.  *  the simulation and start the second pass.  The direction will be the
  761.  *  inverse of the direction just before the simulation started.  We
  762.  *  also reset the font and the horizontal parameters to their saved
  763.  *  values, and set things up so we're rescanning the input just *after*
  764.  *  the BEG_REFLECT that started the simulation.  Finally, we move
  765.  *  "forward" by the total width of the reflected segment;  we'll now
  766.  *  typeset everything "backward" from that point.  (Note that we move
  767.  *  "forward" by the *negative* of /DeltaH/;  this is because we've
  768.  *  already turned the state around.  Exercise:  why must the /PopXW/
  769.  *  precede the /MoveForward/?)  But we're not quite done with /DeltaH/;
  770.  *  we need it at the *end* of the second pass to move forward over
  771.  *  the reflected segment again.  Since the variable /DeltaH/ is used
  772.  *  by any nested simulations, we must save its value on the stack.
  773.  *
  774.  *  If /State/ says we're typesetting (either direction) then we've
  775.  *  just finished the second pass over a reflected segment:  grab the
  776.  *  saved value of /DeltaH/, use it to move back over the reflected
  777.  *  segment, then revert to the previous direction of typesetting
  778.  *  (which is the opposite of the direction just finished).  Be sure
  779.  *  to see the overview if you don't understand this move by /DeltaH/.
  780.  *
  781.  *  Harder exercise:  When we've finished typesetting a simulation as
  782.  *  just described, we repeat the move that we started with to get back
  783.  *  to the right place to continue.  Why don't we use PUSH and POP *in
  784.  *  the output file* to remember the place?  That is, why not add a call
  785.  *  to /PushWrite/ just after /MoveForward(-DeltaH)/ in the SIMULATING
  786.  *  case, and *replace* the /MoveForward/ in the typesetting cases with
  787.  *  a call to /PopWrite/?  We'd save a couple of bytes in the output,
  788.  *  and wouldn't have to worry about the value of /DeltaH/ during the
  789.  *  second pass (thus we could skip pushing and popping /DeltaH/).
  790.  *  What's the disadvantage that vitiates this clever idea?
  791.  */
  792. void
  793. EndReflect()
  794. {
  795.   switch(State) {
  796.     case SIMULATING:
  797.       CurFont = SavedFont;
  798.       ResetFilePosition(SavedPosition);
  799.       State = REVERSE(SavedState);    /* must precede MoveForward */
  800.       PopXW();                /* must precede MoveForward */
  801.       MoveForward(-DeltaH);
  802.       PushDeltaH();
  803.       break;
  804.     case LTYPESETTING: case RTYPESETTING:
  805.       PopDeltaH();
  806.       MoveForward(-DeltaH);
  807.       State = REVERSE(State);
  808.       break;
  809.     default:    /* State >SIMULATING */
  810.       State--;
  811.   }
  812. }
  813.  
  814.  
  815.  
  816. /*
  817.  *  Move "forward" by /distance/.  If we're simulating it's enough to
  818.  *  accumulate /distance/ into the running total, otherwise we need a
  819.  *  motion command in the output file.  Now "forward" means "right" if
  820.  *  typesetting normally, but "left" if typesetting right-to-left, so
  821.  *  first we negate distance if we're in the latter mode.  If we can
  822.  *  accomplish this move with a simple W0 or X0 we do so.  Otherwise,
  823.  *  we use a RIGHTn with the smallest possible n.  Notice that we
  824.  *  test /distance/ against the *output* file's idea of W and X when
  825.  *  trying for the short versions.  These may not agree with W and X
  826.  *  in the input file;  see the two routines immediately following.
  827.  */
  828. void
  829. MoveForward(distance)
  830. long distance;
  831. {
  832.   unsigned bytes;
  833.  
  834.   if (State >= SIMULATING) { DeltaH += distance; return; }
  835.   if (State == RTYPESETTING) distance = -distance;
  836.   if (distance == WOutput) WriteByte(W0);
  837.     else if (distance == XOutput) WriteByte(X0);
  838.     else {
  839.       bytes = SignedBytes(distance);
  840.       WriteByte(RIGHT1 - 1 + bytes);
  841.       WriteNumber(distance, bytes);
  842.   }
  843. }
  844.  
  845.  
  846.  
  847. /*
  848.  *  Process a command in the range W1..W4.  The parameter has been
  849.  *  already been read.  We always need to set this distance into WInput,
  850.  *  the input file's idea of W.  If we're just simulating, just
  851.  *  accumulate /distance/ into the current total and quit (the value of
  852.  *  WOutput is irrelevant during simulation).  If typesetting left-to-
  853.  *  right, output a Wn command to move and set W in the output file.  If
  854.  *  typesetting right-to-left, the same, except we first negate
  855.  *  /distance/.  This means two things: first of all, we'll move the
  856.  *  opposite direction in the output file as desired.  Secondly, the
  857.  *  value of W in the output file (recorded in /WOutput/) is the negative
  858.  *  of its value in the input file.  That way, upcoming W0 commands will
  859.  *  be directly usable as long as we're typesetting backwards.
  860.  */
  861. void
  862. SetWandMoveForward(distance)
  863. long distance;
  864. {
  865.   unsigned bytes;
  866.  
  867.   WInput = WOutput = distance;
  868.   if (State >= SIMULATING) { DeltaH += distance; return; }
  869.   if (State == RTYPESETTING) WOutput = distance = -distance;
  870.   bytes = SignedBytes(distance);
  871.   WriteByte(W1 - 1 + bytes);
  872.   WriteNumber(distance, bytes);
  873. }
  874.  
  875.  
  876.  
  877. /*
  878.  *  Don't even think of finding comments for this routine.  Just look up
  879.  *  at /SetWandMoveForward/, OK?
  880.  */
  881. void
  882. SetXandMoveForward(distance)
  883. long distance;
  884. {
  885.   unsigned bytes;
  886.  
  887.   XInput = XOutput = distance;
  888.   if (State >= SIMULATING) { DeltaH += distance; return; }
  889.   if (State == RTYPESETTING) XOutput = distance = -distance;
  890.   bytes = SignedBytes(distance);
  891.   WriteByte(X1 - 1 + bytes);
  892.   WriteNumber(distance, bytes);
  893. }
  894.  
  895.  
  896.  
  897. /*
  898.  * Process the postamble, having just read a POST command from the input
  899.  *  file.  Mostly we just copy the postamble into the output file, with a
  900.  *  few exceptions: The previous page pointers and maximum stack depth may
  901.  *  be different, and NOP commands are suppressed unless the -X flag was
  902.  *  used.  When we hit POST_POST, we finish off the file with four 223's
  903.  *  and enough more so that the total file length is divisible by four.
  904.  *  Note that ivd2dvi stops reading its input file after seeing the dvi
  905.  *  version character here.  As a consequence, it's always an error for
  906.  *  the input file to come to EOF.
  907.  */
  908. void
  909. Postliminaries()
  910. {
  911.   long i;
  912.  
  913.   if (VerboseOutput) fprintf(stderr, "[post]\n");
  914.   WriteByte(POST);
  915.   SkipNBytes(4L);
  916.   WriteWord(PrevPagePointer);
  917.   PrevPagePointer = BytesOutput - 5;
  918.   CopyNBytes(20L);
  919.   MaxPushLevelOutput();
  920.   CopyNBytes(2L);
  921.   while (CurCommand != POST_POST) {
  922.     CurCommand = ReadByte();
  923.     switch (CurCommand) {
  924.       case FNT_DEF1: case FNT_DEF2: case FNT_DEF3: case FNT_DEF4:
  925.     CopyFontDefinition(ReadSigned(CurCommand - FNT_DEF1 + 1),
  926.                CurCommand - FNT_DEF1 + 1);
  927.     break;
  928.       case NOP:
  929.     if (ExactOutput) WriteByte(CurCommand);
  930.     break;
  931.       case POST_POST:
  932.     break;
  933.       default:
  934.         BadDVIAbort("unrecognized command in postamble");
  935.     }
  936.   }
  937.   WriteByte(POST_POST);
  938.   SkipNBytes(4L);
  939.   WriteWord(PrevPagePointer);
  940.   if (CopyByte() != DVIVERSION)
  941.     BadDVIAbort("wrong DVI version in postamble");
  942.   for (i = 7 - ((BytesOutput-1) % 4); i > 0; i--) WriteByte(DVIPADCHAR);
  943. }
  944.  
  945.  
  946.  
  947. /*
  948.  *  Output the current command, then copy /bytes/ more bytes from the
  949.  *  input file to the output file.  Surprisingly useful.
  950.  */
  951. void
  952. CopyParametrizedCommand(bytes)
  953. unsigned bytes;
  954. {
  955.   WriteByte(CurCommand);
  956.   CopyNBytes((long) bytes);
  957. }
  958.